#include "main.h"
#include "encode.h"
#include "decode.h"
#include "configs.h"
#include <iostream>

// global counters and switches
double average_error_num;
double average_correct_blk_num;
double average_error_blk_num;
double average_final_ber;
double fliprate;
int trial_num;
long BCHk;
GF2X BCHgenerator;

// prints out the information of the experimentation
void print_info(){
  cout << "\n-----------------------------------" << endl;
  cout << "Experiment info" << endl;
  cout << "-----------------------------------" << endl;
  cout << "N:" << BCHn << endl;
  cout << "K:" << BCHk << endl;
  cout << "Rate:" << static_cast<double>(BCHk)/static_cast<double>(BCHn) << endl; 
  cout << "M:" << BCHn - BCHk << endl;
  cout << "p:" << 2 << endl;
  cout << "m:" << BCHm << endl; // don't confuse this m with M which is the number of parity check bits
  cout << "t:" << BCHt << endl;
  cout << "Generator: " << BCHgenerator << endl;
  cout << "Generator degree: " << deg(BCHgenerator) << endl;
  cout << "BSC flip rate:" << fliprate << endl;                                                              
  cout << "Trials:" << trial_num << endl;                                                                    
  cout << "-----------------------------------" << endl;       
}

// start simulation
void simulate_decoding(const GF2X& genpol){
  long errnum;
  vec_GF2 noisy_codeword, pure_codeword;
  vec_GF2 corrected_codeword, errors;
  for(long i = 1; i <= trial_num; ++i){

#ifdef VERBOSE
    std::cout << "Start the " << i << "-th round of test!" << std::endl; 
#endif
    BCHEncoder::generate_random_noisy_codeword(noisy_codeword, pure_codeword, genpol);
    BCHDecoder::decode(corrected_codeword, errors, noisy_codeword);
    errnum = weight(corrected_codeword - pure_codeword);
    average_error_num += static_cast<double>(errnum);
    average_final_ber += static_cast<double>(errnum)/static_cast<double>(BCHn);
#ifdef VERBOSE
    std::cout << "\n";
    std::cout << "The original codeword:" << std::endl;
    std::cout << pure_codeword << std::endl;
    // std::cout << "The noisy codeword:" << std::endl;
    // std::cout << noisy_codeword << std::endl;
    //std::cout << "The corrected bits:" << std::endl;
    //std::cout << corrected_codeword << std::endl;
    std::cout << "Detected errors:" << std::endl;
    std::cout << errors << std::endl;
    std::cout << "Undetected errors:" << std::endl;
    std::cout << noisy_codeword - pure_codeword - errors << std::endl;
    std::cout << "Total number of errors introduced in the channel:" << std::endl;
    std::cout << weight(noisy_codeword - pure_codeword) << std::endl;
    std::cout << "Total number of errors in the corrected codeword:" << std::endl;
    std::cout << weight(corrected_codeword - pure_codeword) << std::endl;
    std::cout << "\n";
#endif
  }
}

// initialize the performance counters
void init_performance_counters() {
  average_error_num = 0;
  average_correct_blk_num = 0;
  average_error_blk_num = 0;
  average_final_ber = 0;
}

// compute the average performance
void compute_average_performance() {
  // average the performance counter
  average_error_num /= trial_num;
  average_final_ber /= trial_num;
}

// prints out the performance results
void print_statistics() {
  compute_average_performance();
  cout << "\n-----------------------------------" << endl;
  cout << "Performance:" << endl;
  cout << "-----------------------------------" << endl;
  cout << "Ave err num: " << average_error_num << endl;
  cout << "Ave BER: " << average_final_ber << endl;
  cout << "-----------------------------------" << endl;
}

int main(int argc, char* argv[]){  
  if(argc != 3){                                     
    cout << "incorrect number of parameters" << endl;
    exit(EXIT_FAILURE); 
  }
  trial_num = atoi(argv[1]);    
  fliprate = atof(argv[2]);
  GF2X genpol;
  BCHEncoder::compute_generator_polynomial(genpol);
  BCHgenerator = genpol;
  BCHk = BCHn - deg(BCHgenerator);

  init_performance_counters();
  simulate_decoding(genpol);
  print_info();
  print_statistics();
  return 0;
}


